home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1995 October
/
EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso
/
Aminet
/
mus
/
misc
/
maplay1_2.lha
/
maplay
/
obuffer.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-26
|
19KB
|
784 lines
/*
* @(#) obuffer.cc 1.17, last edit: 6/27/94 13:11:31
* @(#) Copyright (C) 1993, 1994 Tobias Bading (bading@cs.tu-berlin.de)
* @(#) Berlin University of Technology
*
* Idea and first implementation for u-law output with fast downsampling by
* Jim Boucher (jboucher@flash.bu.edu)
*
* LinuxObuffer class written by
* Louis P. Kruger (lpkruger@phoenix.princeton.edu)
*
* IffObuffer class written by
* Erik Johannessen (erik2@afrodite.kih.no)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Changes from version 1.1 to 1.2:
* - new LinuxObuffer class, which works with 16 bit soundcards
* like Gravis Ultrasound, SoundBlaster 16 or Pro Audio Spectrum 16,
* if attached to /dev/dsp
* - ShortObuffer renamed to FileObuffer
* - If ULAW is defined:
* - SparcObuffer feeds mono u-law output to an amd device (u-law format).
* The required downsampling to 8 kHz in done in the synthesis filter.
* - FileObuffer creates u-law output at 8 kHz instead of 16 bit PCM samples.
* - O_NDELAY flag is cleared now after a successful open syscall
* on /dev/audio (caused problems under Solaris)
* - options -us, -uh and -ul should work now
* - FileObuffer can handle incomplete write syscalls now
*/
/*
* 19. Feb. 1995: IffObuffer class added by Erik Johannessen
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <iostream.h>
#include "obuffer.h"
#include "header.h"
#ifdef ULAW
#include "ulaw.h"
#endif
#ifdef Iff
typedef long LONG;
typedef unsigned long ULONG;
typedef unsigned short UWORD;
typedef unsigned char UBYTE;
/* ---------- Voice8Header ---------------------------------------------*/
typedef LONG Fixed; /* A fixed-point value, 16 bits to the left of
* the point and 16 to the right. A Fixed is a
* number of 2**16ths, i.e. 65536ths. */
#define Unity 0x10000L /* Unity = Fixed 1.0 = maximum volume */
/* sCompression: Choice of compression algorithm applied to the samples. */
#define sCmpNone 0 /* not compressed */
#define sCmpFibDelta 1 /* Fibonacci-delta encoding (Appendix C) */
/* Could be more kinds in the future. */
typedef struct {
ULONG oneShotHiSamples, /* # samples in the high octave 1-shot part */
repeatHiSamples, /* # samples in the high octave repeat part */
samplesPerHiCycle; /* # samples/cycle in high octave, else 0 */
UWORD samplesPerSec; /* data sampling rate */
UBYTE ctOctave, /* # of octaves of waveforms */
sCompression; /* data compression technique used */
Fixed volume; /* playback nominal volume from 0 to Unity
* (full volume). Map this value into
* the output hardware's dynamic range.
*/
} Voice8Header;
#endif // Iff
#ifdef SunOS
extern "C" int ioctl (int, int ...); // Why...???
#endif
FileObuffer::FileObuffer (uint32 number_of_channels)
{
#ifdef DEBUG
if (!number_of_channels || number_of_channels > MAXCHANNELS)
{
cerr << "FileObuffer: number of channels has to be in [1, " << MAXCHANNELS << "] !\n";
exit (1);
}
#endif
#ifdef ULAW
if (number_of_channels > 1)
cerr << "Are you sure you need stereo u-law output?\n";
#endif
channels = number_of_channels;
for (int i = 0; i < number_of_channels; ++i)
bufferp[i] = buffer + i;
}
void FileObuffer::append (uint32 channel, int16 value)
{
#ifdef DEBUG
if (channel >= channels)
{
cerr << "illegal channelnumber in FileObuffer::append()!\n";
exit (1);
}
if (bufferp[channel] - buffer >= OBUFFERSIZE)
{
cerr << "FileObuffer: buffer overflow!\n";
exit (1);
}
#endif
#ifdef ULAW
// convert 16-bit PCM sample to 8-bit ulaw:
*bufferp[channel] = linear2ulaw[value >> 3];
#else
*bufferp[channel] = value;
#endif
bufferp[channel] += channels;
}
void FileObuffer::write_buffer (int fd)
{
int length = (int)((char *)bufferp[0] - (char *)buffer), writelength;
if ((writelength = write (fd, (char *)buffer, length)) != length)
{
// buffer has not been written completely
if (writelength < 0)
{
perror ("write");
exit (1);
}
length -= writelength;
char *buffer_pos = (char *)buffer;
do
{
buffer_pos += writelength;
if ((writelength = write (fd, buffer_pos, length)) < 0)
{
perror ("write");
exit (1);
}
}
while (length -= writelength);
}
for (int i = 0; i < channels; ++i)
bufferp[i] = buffer + i;
}
#ifdef Iff
// This Iff 8SVX code is just a quick hack.
// Please note that the stereo output mode hasn't been testet much.
// Erik Johannessen (erik2@afrodite.kih.no)
IffObuffer::IffObuffer (uint32 number_of_channels, Header *header, char *filename)
{
unsigned long dummy=0;
Voice8Header vhdr;
char *fname;
long chan_type;
char tmpstr[32];
channels = number_of_channels;
for (int i = 0; i < number_of_channels; ++i)
bufferp[i] = buffer + i*(OBUFFERSIZE>>1);
if(channels==2){
chan_type=6L;
strcpy(fname2,"/tmp/maplay.");
sprintf(tmpstr,"%ld",getpid());
strcat(fname2,tmpstr);
fout2=fopen(fname2,"wb+");
if(fout2==NULL){
cerr << "Iff can't open output file for channel 1\n";
exit(1);
}
}
else
chan_type=2L;
fname=(char *)malloc(strlen(filename)+6);
strcpy(fname,filename);
strcat(fname,".8svx");
fout=fopen(fname,"wb");
if(fout==NULL){
cerr << "Iff can't open output file for channel 0\n";
exit(1);
}
free(fname);
fwrite("FORM",1,4,fout);
fwrite(&dummy,sizeof(dummy),1,fout);
fwrite("8SVX",1,4,fout);
fwrite("VHDR",1,4,fout);
dummy=sizeof(vhdr);
fwrite(&dummy,sizeof(dummy),1,fout);
vhdr.oneShotHiSamples=0;
vhdr.repeatHiSamples=0;
vhdr.samplesPerHiCycle=0;
vhdr.samplesPerSec=header->frequency();
vhdr.ctOctave=1;
vhdr.sCompression=sCmpNone;
vhdr.volume=Unity;
fwrite(&vhdr,sizeof(vhdr),1,fout);
dummy=sizeof(chan_type);
fwrite("CHAN",1,4,fout);
fwrite(&dummy,sizeof(dummy),1,fout);
fwrite(&chan_type,sizeof(chan_type),1,fout);
fwrite("BODY",1,4,fout);
dummy=0;
fwrite(&dummy,sizeof(dummy),1,fout);
totbytes=0;
}
IffObuffer::~IffObuffer(void)
{
unsigned long size;
char trbuf[2048];
long cnt;
if(channels==2){
fseek(fout2,0,SEEK_SET);
do{
cnt=fread(trbuf,1,2048,fout2);
fwrite(trbuf,1,cnt,fout);
}while(cnt==2048);
fclose(fout2);
remove(fname2);
}
size=totbytes+4+4+sizeof(Voice8Header)+12+4+4+4;
fseek(fout,4,SEEK_SET);
fwrite(&size,sizeof(size),1,fout);
fseek(fout,4+4+4+4+4+sizeof(Voice8Header)+12+4,SEEK_SET);
fwrite(&totbytes,sizeof(totbytes),1,fout);
fseek(fout,4+4+4+4+4,SEEK_SET);
fwrite(&totbytes,sizeof(totbytes),1,fout);
fclose(fout);
}
void IffObuffer::append(uint32 channel,int16 value)
{
// cerr << "channel " << channel << "\n";
*bufferp[channel] = value>>8;
bufferp[channel] ++;
}
void IffObuffer::write_buffer(int)
{
int length = (int)((char *)bufferp[0] - (char *)buffer);
fwrite((char *)buffer,length,1,fout);
if(channels==2){
fwrite((char *)buffer+(OBUFFERSIZE>>1),length,1,fout2);
length<<=1;
}
totbytes+=length;
for (int i = 0; i < channels; ++i)
bufferp[i] = buffer + i*(OBUFFERSIZE>>1);
}
#endif // Iff
#ifdef Indigo
IndigoObuffer::IndigoObuffer (uint32 number_of_channels, Header *header)
{
#ifdef DEBUG
if (!number_of_channels || number_of_channels > MAXCHANNELS)
{
cerr << "IndigoObuffer: number of channels has to be in [1, " << MAXCHANNELS << "] !\n";
exit (1);
}
#endif
channels = number_of_channels;
for (int i = 0; i < number_of_channels; ++i)
bufferp[i] = buffer + i;
// open an audio port and configure it:
ALconfig config;
if (!(config = ALnewconfig ()))
{
cerr << "ALnewconfig failed!\n";
exit (1);
}
ALsetwidth (config, AL_SAMPLE_16);
if (channels == 1)
ALsetchannels (config, AL_MONO);
else
ALsetchannels (config, AL_STEREO);
if (!(port = ALopenport ("MPEG audio player", "w", config)))
{
cerr << "can't allocate an audio port!\n";
exit (1);
}
// set sample rate:
long pvbuffer[2] = { AL_OUTPUT_RATE, 0 };
pvbuffer[1] = header->frequency ();
ALsetparams (AL_DEFAULT_DEVICE, pvbuffer, 2);
ALfreeconfig (config);
}
IndigoObuffer::~IndigoObuffer (void)
{
while (ALgetfilled (port) > 0)
sleep (1);
ALcloseport (port);
}
void IndigoObuffer::append (uint32 channel, int16 value)
{
#ifdef DEBUG
if (channel >= channels)
{
cerr << "illegal channelnumber in IndigoObuffer::append()!\n";
exit (1);
}
if (bufferp[channel] - buffer >= OBUFFERSIZE)
{
cerr << "IndigoObuffer: buffer overflow!\n";
exit (1);
}
#endif
*bufferp[channel] = value;
bufferp[channel] += channels;
}
void IndigoObuffer::write_buffer (int)
{
ALwritesamps (port, buffer, (long)(bufferp[0] - buffer));
for (int i = 0; i < channels; ++i)
bufferp[i] = buffer + i;
}
#endif // Indigo
#ifdef SPARC
int SparcObuffer::audio_fd = -1;
#ifdef ULAW
SparcObuffer::SparcObuffer (Header *header, bool use_speaker, bool use_headphone, bool use_line_out)
#else
SparcObuffer::SparcObuffer (uint32 number_of_channels, Header *header,
bool use_speaker, bool use_headphone, bool use_line_out)
#endif
{
#ifndef ULAW
#ifdef DEBUG
if (!number_of_channels || number_of_channels > MAXCHANNELS)
{
cerr << "SparcObuffer: 0 < number of channels < " << MAXCHANNELS << "!\n";
exit (1);
}
#endif
#endif // !ULAW
if (audio_fd < 0)
{
cerr << "Internal error: SparcObuffer::audio_fd has to be initialized\n"
"by SparcObuffer::class_suitable()!\n";
exit (1);
}
audio_info info;
AUDIO_INITINFO (&info);
#ifndef SunOS4_1_1
info.output_muted = False;
#endif
info.play.port = 0;
if (use_speaker)
info.play.port |= AUDIO_SPEAKER;
if (use_headphone)
info.play.port |= AUDIO_HEADPHONE;
#ifndef SunOS4_1_1
if (use_line_out)
info.play.port |= AUDIO_LINE_OUT;
#endif
#ifdef ULAW
bufferp = buffer;
// configure the amd device:
info.play.encoding = AUDIO_ENCODING_ULAW;
info.play.precision = 8;
info.play.channels = 1;
info.play.sample_rate = 8000;
#else
channels = number_of_channels;
for (int i = 0; i < number_of_channels; ++i)
bufferp[i] = buffer + i;
// configure the dbri device:
info.play.encoding = AUDIO_ENCODING_LINEAR;
info.play.precision = 16;
info.play.channels = channels;
info.play.sample_rate = header->frequency ();
#endif // !ULAW
if (ioctl (audio_fd, AUDIO_SETINFO, &info))
{
perror ("configuration of /dev/audio failed");
exit (1);
}
}
SparcObuffer::~SparcObuffer (void)
{
ioctl (audio_fd, AUDIO_DRAIN, NULL);
close (audio_fd);
}
void SparcObuffer::append (uint32 channel, int16 value)
{
#ifdef ULAW
#ifdef DEBUG
if (bufferp - buffer >= OBUFFERSIZE >> 1)
{
cerr << "SparcObuffer: buffer overflow!\n";
exit (1);
}
#endif
// convert 16-bit PCM sample to 8-bit ulaw:
*bufferp++ = linear2ulaw[value >> 3];
#else
#ifdef DEBUG
if (channel >= channels)
{
cerr << "illegal channelnumber in SparcObuffer::append()!\n";
exit (1);
}
if (bufferp[channel] - buffer >= OBUFFERSIZE)
{
cerr << "SparcObuffer: buffer overflow!\n";
exit (1);
}
#endif
*bufferp[channel] = value;
bufferp[channel] += channels;
#endif // !ULAW
}
void SparcObuffer::write_buffer (int)
{
#ifdef ULAW
int length = (int)((char *)bufferp - (char *)buffer);
#else
int length = (int)((char *)*bufferp - (char *)buffer);
#endif
if (write (audio_fd, (char *)buffer, length) != length)
{
perror ("write to /dev/audio failed");
exit (1);
}
#ifdef ULAW
bufferp = buffer;
#else
for (int i = 0; i < channels; ++i)
bufferp[i] = buffer + i;
#endif
}
int SparcObuffer::open_audio_device (void)
{
int fd;
if ((fd = open ("/dev/audio", O_WRONLY | O_NDELAY, 0)) < 0)
if (errno == EBUSY)
{
cerr << "Sorry, the audio device is busy!\n";
exit (1);
}
else
{
perror ("can't open /dev/audio for writing");
exit (1);
}
// turn NDELAY mode off:
int flags;
if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
{
perror ("fcntl F_GETFL on /dev/audio failed");
exit (1);
}
flags &= ~O_NDELAY;
if (fcntl (fd, F_SETFL, flags) < 0)
{
perror ("fcntl F_SETFL on /dev/audio failed");
exit (1);
}
return fd;
}
#ifdef Solaris
void SparcObuffer::get_device_type (int fd, audio_device *devtype)
{
if (ioctl (fd, AUDIO_GETDEV, devtype))
{
perror ("ioctl AUDIO_GETDEV on /dev/audio");
exit (1);
}
}
#else
int SparcObuffer::get_device_type (int fd)
{
#ifdef AUDIO_GETDEV
int devtype;
if (ioctl (fd, AUDIO_GETDEV, &devtype))
{
perror ("ioctl AUDIO_GETDEV on /dev/audio");
exit (1);
}
return devtype;
#else
cerr << "SparcObuffer::get_device_type(): AUDIO_GETDEV ioctl not available!\n";
return -1;
#endif
}
#endif // !Solaris
#ifdef ULAW
bool SparcObuffer::class_suitable (uint32 number_of_channels, bool force_amd)
#else
bool SparcObuffer::class_suitable (void)
#endif
{
#ifdef ULAW
if (number_of_channels > 1)
{
cerr << "Your audio hardware cannot handle more than one audio channel.\n"
"Please use the option -l or -r for stereo streams.\n";
return False;
}
#endif
// check for the dbri audio device:
audio_fd = open_audio_device ();
#ifdef ULAW
if (force_amd)
return True;
#endif
#ifdef Solaris
audio_device devtype;
get_device_type (audio_fd, &devtype);
# ifdef ULAW
if (!strcmp (devtype.name, "SUNW,am79c30"))
return True;
else if (!strcmp (devtype.name, "SUNW,dbri"))
{
cerr << "Your machine can produce CD-quality audio output,\n"
"but this binary was compiled for 8 kHz u-law ouput. (telephone quality)\n"
"Please recompile it without the ULAW define in COMPILERFLAGS.\n"
"(or use the -amd option to use this binary with low-quality output)\n";
close (audio_fd);
return False;
}
# else
if (!strcmp (devtype.name, "SUNW,dbri"))
return True;
else if (!strcmp (devtype.name, "SUNW,am79c30"))
{
cerr << "Your machine can produce 8 kHz u-law audio output only,\n"
"but this binary was compiled for CD-quality output.\n"
"Please recompile it with ULAW defined in COMPILERFLAGS\n"
"or use it in stdout mode as an decoder only.\n";
close (audio_fd);
return False;
}
# endif
#else
// !Solaris
# ifdef SunOS4_1_1
// no AUDIO_GETDEV under SunOS 4.1.1, so we have to assume that there is
// an amd device attached to /dev/audio
# ifdef ULAW
return True;
# else
cerr << "Your machine can produce 8 kHz u-law audio output only,\n"
"but this binary was compiled for CD-quality output.\n"
"Please recompile it with ULAW defined in COMPILERFLAGS\n"
"or use it in stdout mode as an decoder only.\n";
close (audio_fd);
return False;
# endif // !ULAW
# else
// SunOS 4.1.3
int device_type = get_device_type (audio_fd);
# ifdef ULAW
if (device_type == AUDIO_DEV_AMD)
return True;
else if (device_type == AUDIO_DEV_SPEAKERBOX)
{
cerr << "Your machine can produce CD-quality audio output,\n"
"but this binary was compiled for 8 kHz u-law ouput. (telephone quality)\n"
"Please recompile it without the ULAW define in COMPILERFLAGS.\n"
"(or use the -amd option to use this binary with low-quality output)\n";
close (audio_fd);
return False;
}
# else
if (device_type == AUDIO_DEV_SPEAKERBOX)
return True;
else if (device_type == AUDIO_DEV_AMD)
{
cerr << "Your machine can produce 8 kHz u-law audio output only,\n"
"but this binary was compiled for CD-quality output.\n"
"Please recompile it with ULAW defined in COMPILERFLAGS\n"
"or use it in stdout mode as an decoder only.\n";
close (audio_fd);
return False;
}
# endif // !ULAW
# endif // !SunOS4_1_1
#endif // !Solaris
#ifndef SunOS4_1_1
close (audio_fd);
cerr << "Sorry, I don't recognize your audio device.\n"
# ifdef ULAW
"Please try the -amd option or use the stdout mode.\n";
# else
"Please use the stdout mode.\n";
# endif
return False;
#endif
}
#endif // SPARC
#ifdef LINUX
int LinuxObuffer::audio_fd = -1;
int LinuxObuffer::open_audio_device (void)
{
int fd;
if ((fd = open ("/dev/dsp", O_WRONLY | O_NDELAY, 0)) < 0)
if (errno == EBUSY)
{
cerr << "Sorry, the audio device is busy!\n";
exit (1);
}
else
{
perror ("can't open /dev/dsp for writing");
exit (1);
}
// turn NDELAY mode off:
int flags;
if ((flags = fcntl (fd, F_GETFL, 0)) < 0)
{
perror ("fcntl F_GETFL on /dev/audio failed");
exit (1);
}
flags &= ~O_NDELAY;
if (fcntl (fd, F_SETFL, flags) < 0)
{
perror ("fcntl F_SETFL on /dev/audio failed");
exit (1);
}
return fd;
}
LinuxObuffer::LinuxObuffer (uint32 number_of_channels, Header *header)
{
#ifdef DEBUG
if (!number_of_channels || number_of_channels > MAXCHANNELS)
{
cerr << "LinuxObuffer: 0 < number of channels < " << MAXCHANNELS << "!\n";
exit (1);
}
#endif
channels = number_of_channels;
for (int i = 0; i < number_of_channels; ++i)
bufferp[i] = buffer + i;
if (audio_fd < 0)
{
cerr << "Internal error, LinuxObuffer::audio_fd has to be initialized\n"
"by LinuxObuffer::class_suitable()!\n";
exit (1);
}
// configure the device:
int play_precision = 16;
int play_stereo = channels-1;
int play_sample_rate = header->frequency ();
if(
ioctl(audio_fd, SNDCTL_DSP_SAMPLESIZE, &play_precision) == -1 ||
ioctl(audio_fd, SNDCTL_DSP_STEREO, &play_stereo) == -1 ||
ioctl(audio_fd, SNDCTL_DSP_SPEED, &play_sample_rate) == -1
)
{
perror ("configuration of /dev/dsp failed");
exit (1);
}
}
LinuxObuffer::~LinuxObuffer (void)
{
sleep (1);
close (audio_fd);
}
void LinuxObuffer::append (uint32 channel, int16 value)
{
#ifdef DEBUG
if (channel >= channels)
{
cerr << "illegal channelnumber in LinuxObuffer::append()!\n";
exit (1);
}
if (bufferp[channel] - buffer >= OBUFFERSIZE)
{
cerr << "buffer overflow!\n";
exit (1);
}
#endif
*bufferp[channel] = value;
bufferp[channel] += channels;
}
void LinuxObuffer::write_buffer (int)
{
int length = (int)((char *)bufferp[0] - (char *)buffer);
if (write (audio_fd, buffer, length) != length)
{
perror ("write to /dev/dsp failed");
exit (1);
}
for (int i = 0; i < channels; ++i)
bufferp[i] = buffer + i;
}
bool LinuxObuffer::class_suitable (uint32 number_of_channels)
{
// open the dsp audio device:
audio_fd = open_audio_device ();
return True;
}
#endif /* LINUX */